from clusterhelperform import Ui_ClusterHelperForm
from mailsform import Ui_MailsForm
import sys, ConfigObj, _pg, pickle
from PyQt4 import QtCore, QtGui

class Alias:
	def __init__(self, email, name, count):
		self.name = name
		self.email = email
		self.count = count
		self.cluster = AliasCluster(self)
		
	def setNotSamePerson(self, alias):
		self.cluster.addToNotSameList(alias.cluster)

class AliasCluster:
	def __init__(self, alias):
		#this holds alias objects
		self.aliases = [alias]
		#this holds email address strings
		self.notSameList = set()
		#this tells us if something has already been done to this cluster
		self.flag = False
		
	def merge(self, cluster):
		#if cluster in self.notSameList:
		#	print "ERROR: trying to merge to clusters in the not same list! Won't do it"
		#	return
		if cluster == self:
			print "WARNING: merging a cluster with itself"
			print "cluster contains aliases"
			for alias in self.aliases:
				print "  " + alias.email, alias.name, alias.count
			return
		for alias in cluster.aliases:
			alias.cluster = self
			self.aliases.append(alias)
		self.notSameList.update(cluster.notSameList)
		
	def addToNotSameList(self, cluster):
		if cluster == self:
			print "ERROR: calling addToNotSameList on itself!"
		for alias in cluster.aliases:
			self.notSameList.add(alias.email)
			
class Pair:
	NOT_DECIDED = "NOT_DECIDED"
	SAME = "SAME"
	NOT_SAME = "NOT_SAME"
	PROBABLY_NOT = "PROBABLY_NOT"
	def __init__(self, email1, email2):
		self.email1 = email1
		self.email2 = email2
		self.emails = (email1, email2)
		self.status = self.NOT_DECIDED
		
	def __repr__(self):
		return repr( (self.email1, self.email2, self.status) )
		
		

class ClusterHelper(QtGui.QMainWindow, Ui_ClusterHelperForm):
	def __init__(self, config):
		self.config = config
		QtGui.QMainWindow.__init__(self)
		self.setupUi(self)
		self.connect(self.actionLoad_Files, QtCore.SIGNAL("activated()"), self.loadFilesSlot)
		self.connect(self.actionConnect_to_DB, QtCore.SIGNAL("activated()"), self.connectToDBSlot)
		self.connect(self.samePushButton, QtCore.SIGNAL("clicked()"), self.samePushButtonClickedSlot)
		self.connect(self.notSamePushButton, QtCore.SIGNAL("clicked()"), self.notSamePushButtonClickedSlot)
		self.connect(self.actionPickle_Data, QtCore.SIGNAL("activated()"), self.pickleData)
		self.connect(self.actionUnpickle_Data, QtCore.SIGNAL("activated()"), self.unpickleData)
		self.connect(self.actionCommit_to_DB, QtCore.SIGNAL("activated()"), self.writeClusters)
		self.connect(self.viewMails1PushButton, QtCore.SIGNAL("clicked()"), self.showMails1)
		self.connect(self.viewMails2PushButton, QtCore.SIGNAL("clicked()"), self.showMails2)
		self.comparisons = 0
		self.test()
		self.l = []
		
	def test(self):
		self.connectToDBSlot()
		self.loadFilesSlot()

	def showMails1(self):
		self.showMails(self.alias1)

	def showMails2(self):
		self.showMails(self.alias2)
	
	def showMails(self, alias):
		mailsForm = MailsViewer(alias, self.db)
		print "about to show"
		mailsForm.show()
		#mailsForm.raise()
		mailsForm.activateWindow()
		self.l.append(mailsForm)
		
	def loadFilesSlot(self):
		print "loading files"

		self.aliasPairs = []
		
		#first load the perfect pairs
		for line in open("pairs"):
			percent, id1, id2 = [x.strip() for x in line.split("|")]
			email1 = id1.split()[0].strip()
			email2 = id2.split()[0].strip()
			pair = Pair(email1, email2)
			self.aliasDict[email1].cluster.merge(self.aliasDict[email2].cluster)
			self.aliasPairs.append( Pair(email1, email2) )
		
		#next load the imperfect pairs
		for line in open("imperfectPairs"):
			percent, id1, id2 = [x.strip() for x in line.split("|")]
			email1 = id1.split()[0].strip()
			email2 = id2.split()[0].strip()
			self.aliasPairs.append( Pair(email1, email2) )
			
		print self.aliasPairs

		self.showNextPair()		
		
	def connectToDBSlot(self):

		#extract sql login info and connect to database
		dbName = self.config['dbName']
		dbHost = self.config['dbHost']
		dbUser = self.config['dbUser']
		dbPass = self.config['dbPass']

		try:
			print "connecting to database..."
			self.db = _pg.connect(dbname=dbName,host=dbHost,passwd=dbPass,user=dbUser)
			print "...OK!"
		except _pg.InternalError:	
			print "Could not connect to database", dbName
			sys.exit(2)
		sql = """select email, name, count(*) from messages, aliases where messages.senderaliasid = aliases.aliasid
			group by email, name"""
			
		self.aliasDict = {}
		for email, name, count in self.db.query(sql).getresult():
			if "testers" in email:
				print email, name, count
			alias = Alias(email, name, count)
			self.aliasDict[alias.email] = alias
			
	def showNextPair(self):
		self.updateStatuses()
		for aliasPair in self.aliasPairs:
			if aliasPair.status == Pair.NOT_DECIDED:
				break
		self.pair = aliasPair
		self.email1, self.email2 = self.pair.emails
		self.alias1 = self.aliasDict[self.email1]
		self.alias2 = self.aliasDict[self.email2]
		self.showPair()
			
	def showPair(self):	
		self.comparisonsMadeLabel.setText("Comparisons Made: " + str(self.comparisons))	
		self.name1LineEdit.setText(self.alias1.name)
		self.email1LineEdit.setText(self.email1)
		self.mailsSent1.setText("Sent Mails: " + str(self.alias1.count))
		self.name2LineEdit.setText(self.alias2.name)
		self.email2LineEdit.setText(self.email2)
		self.mailsSent2.setText("Sent Mails: " + str(self.alias2.count))
		
	def samePushButtonClickedSlot(self):
		print "same clicked"
		self.pair.status = Pair.SAME
		self.alias1.cluster.merge(self.alias2.cluster)
		self.postClick()
				
		
	def notSamePushButtonClickedSlot(self):
		print "not clicked"
		self.pair.status = Pair.NOT_SAME
		self.alias1.cluster.addToNotSameList(self.alias2.cluster)
		self.alias2.cluster.addToNotSameList(self.alias1.cluster)
		self.postClick()
		
	def postClick(self):
		self.updateNames()
		self.updateStatuses()
		self.comparisons += 1
		self.showNextPair()
	
	def updateNames(self):
		sqlTemplate = "UPDATE aliases SET name = '%s' WHERE email = '%s'"
		if self.name1LineEdit.isModified():
			answer = QtGui.QMessageBox.question(self, "Update Name", 
				"""<qt>You updated the name for email %s to %s, 
				would you like to update the name in the database?</qt>""" % (self.email1, self.name1LineEdit.text()),
					QtGui.QMessageBox.Yes, QtGui.QMessageBox.No, QtGui.QMessageBox.NoButton)
			if answer == QtGui.QMessageBox.Yes:
				self.db.query(sqlTemplate % (self.name1LineEdit.text(), self.email1))
				self.aliasDict[self.email1].name = str(self.name1LineEdit.text())
		if self.name2LineEdit.isModified():
			answer = QtGui.QMessageBox.question(self, "Update Name", 
				"""<qt>You updated the name for email %s to %s, 
				would you like to update the name in the database?</qt>""" % (self.email2, self.name2LineEdit.text()),
					QtGui.QMessageBox.Yes, QtGui.QMessageBox.No, QtGui.QMessageBox.NoButton)
			if answer == QtGui.QMessageBox.Yes:
				self.db.query(sqlTemplate % (self.name2LineEdit.text(), self.email2))
				self.aliasDict[self.email2].name = str(self.name2LineEdit.text())
				
	def updateStatuses(self):
		keys = self.aliasDict.keys()
		keys.sort()
		print keys
		count = 0
		for pair in self.aliasPairs:
			email1, email2 = pair.emails
			cluster1 = self.aliasDict[email1].cluster
			cluster2 = self.aliasDict[email2].cluster
			if pair.status == Pair.NOT_DECIDED:
				if cluster1 == cluster2:
					pair.status = Pair.SAME
				elif email1 in cluster2.notSameList or email2 in cluster1.notSameList:
					pair.status = Pair.NOT_SAME
				else:
					count += 1
		self.pairsLeftLabel.setText("Pairs Left: " + str(count))
		
	def writeClusters(self):
		print "committing data to the database"
		sql = "UPDATE aliases SET personid = NULL"
		self.db.query(sql)
		sql = "DELETE FROM people"
		self.db.query(sql)
		for alias in self.aliasDict.values():
			print alias.email
			if alias.cluster.flag:
				continue
			alias.cluster.flag = True
			names = {}
			for a in alias.cluster.aliases:
				#only count names that are not the empty string
				if a.name.strip():
					names[a.name.strip()] = names.get(a.name.strip(), 0) + 1
			maxName = alias.email
			maxCount = 0
			for name, count in names.items():
				if count > maxCount:
					maxCount = count
					maxName = name
			
			sql = "INSERT INTO people (name) values('%s')" % maxName
			self.db.query(sql)
			
			sql = "select currval('people_personid_seq')"
			for (personid,) in self.db.query(sql).getresult():
				print personid
				
			for a in alias.cluster.aliases:
				sql = "UPDATE aliases SET personid = %i WHERE email = '%s'" % (personid, a.email)
				self.db.query(sql)
		print "done"
			
			
	def pickleData(self):
		pickler = pickle.Pickler(open("pickleddata", "w"))
		pickler.dump(self.aliasDict)
		pickler.dump(self.aliasPairs)
		
	def unpickleData(self):
		pickler = pickle.Unpickler(open("pickleddata"))
		self.aliasDict = pickler.load()
		self.aliasPairs = pickler.load()
		
		print self.aliasDict
		print self.aliasPairs
		self.updateStatuses()
		self.showNextPair()
	
class MailsViewer(QtGui.QDialog, Ui_MailsForm):
	def __init__(self, alias, db):
		self.alias = alias
		self.db = db
		QtGui.QDialog.__init__(self)
		self.setupUi(self)
		self.connect(self.mailsTreeWidget, QtCore.SIGNAL("currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)"),\
			self.itemActivated)
		self.authorLabel.setText("Mails sent by " + alias.email)

		self.getMails()

	def getMails(self):
		sql = """select datetime, subject, messageid from messages, aliases where aliases.email='%s' and 
			messages.senderaliasid = aliases.aliasid order by datetime asc""" % self.alias.email
		for date, subject, mid in self.db.query(sql).getresult():
			item = QtGui.QTreeWidgetItem(self.mailsTreeWidget)
			item.setText(0, date)
			item.setText(1, subject)
			item.messageid = mid

	def itemActivated(self, item):
		print "item is activated"
		print item.messageid
		sql = """select body from messages where messageid='%s'""" % item.messageid
		body = self.db.query(sql).getresult()[0][0]
		self.mailTextEdit.setPlainText(body)
			
		
def runGui():
	app = QtGui.QApplication(sys.argv)
    config = ConfigObj.ConfigObj(sys.argv[1])
	clusterHelper = ClusterHelper(config)
	clusterHelper.show()
	app.exec_()
		
		
if __name__ == "__main__":
	runGui()
